package server;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;

public class IDHttpThread implements Runnable
{
	private Socket sock = null;
	private IDInstance env = null;
	private IDHttpListener server = null;
	
	private Thread myThread = null;
	
	public IDHttpThread(Socket sock, IDInstance env, IDHttpListener server)
	{
		this.sock = sock;
		this.env = env;
		this.server = server;
		
		myThread = new Thread(this);
	}
	
	public void startThread()
	{
		myThread.start();
	}
	
	public static boolean serveStream(String name, InputStream is, OutputStream os, boolean mustBeFresh) throws UnsupportedEncodingException, IOException
	{
		os.write("HTTP/1.1 200 OK\n".getBytes("UTF-8"));
		os.write("Content-Type: application/octet-stream\n".getBytes("UTF-8"));
		os.write(("Content-Disposition: attachment; filename=" + name + "\n").getBytes("UTF-8"));
		os.write("Content-Transfer-Encoding: binary\n".getBytes("UTF-8"));
		if(mustBeFresh)
		{
			os.write("Expires: 0\n".getBytes("UTF-8"));
			os.write("Cache-Control: must-revalidate, post-check=0, pre-check=0\n".getBytes("UTF-8"));
		}
		os.write("Pragma: public\n".getBytes("UTF-8"));
		os.write(("Content-Length: " + is.available() + "\n").getBytes("UTF-8"));
		os.write("\n".getBytes("UTF-8"));
		
		int bufferMax = (int)Math.min(is.available(), 64 * 1024);
		byte[] buffer = new byte[bufferMax];
		
		while(true)
		{
			int rb = is.read(buffer);
			if(rb > 0)
			{
				os.write(buffer, 0, rb);
			}
			else
			{
				break;
			}
		}
		
		is.close();
		os.close();
		return true;
	}
	
	public static boolean serveFile(File file, OutputStream os, boolean mustBeFresh)
	{
		if(file == null || !file.exists())
		{
			try {
				os.close();
			} catch (IOException e) {}
			return false;
		}
		
		System.out.println("Serving file: '" + file.getName() + "'");
		
		try 
		{
			FileInputStream fis = new FileInputStream(file);
			
			try
			{
				serveStream(file.getName(), fis, os, mustBeFresh);
			}
			catch(Exception e)
			{
				System.out.println("Fail at serving " + file.getName());
			}
		} 
		catch (IOException e) 
		{
			System.out.println("FAIL : " + e.getMessage());
			return false;
		}
		
		System.out.println("File served: '" + file.getName() + "'");
		
		return true;
	}
	
	public static String collapseSlashes(String input)
	{
		StringBuilder sb = new StringBuilder();
		char[] array = input.toCharArray();
		
		boolean lastSlash = false;
		for(char c : array)
		{
			if(c == '/' || c == '\\')
			{
				if(!lastSlash)
				{
					lastSlash = true;
					sb.append(c);
				}
			}
			else
			{
				lastSlash = false;
				sb.append(c);
			}
		}
		
		return sb.toString();
	}

	@Override
	public void run() 
	{
		System.out.println("Started a thread...");
		
		BufferedReader in;
		try 
		{
			in = new BufferedReader(new InputStreamReader(
			    sock.getInputStream()));
		
	        PrintWriter out = new PrintWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8")); //new PrintWriter(sock.getOutputStream());
	
	        // read the data sent. We basically ignore it,
	        // stop reading once a blank line is hit. This
	        // blank line signals the end of the client HTTP
	        // headers.
	        String str = ".";
	        boolean first = true;
	        
	        String method = "";
	        String path = "";
	        @SuppressWarnings("unused")
			String version = "";
	        
	        while (!str.equals(""))
	        {
	          str = in.readLine();
	          if(str == null)
	        	  break;
	          
	          //System.out.println(str);
	          if(first)
	          {
	        	  first = false;
	        	  String[] parts = str.split(" ");
	        	  if(parts.length != 3)
	        	  {
	        		  System.out.println("Invalid/misunderstood request");
	        		
	        		  out.flush();
	      	          sock.close();
	        		  System.out.println("Ended a thread.");
	        		  server.unregisterThread(this);
	        		  return;
	        	  }
	        	  else
	        	  {
	        		  method = parts[0];
	        		  path = parts[1];
	        		  version = parts[2];
	        	  }
	          }
	        }
	        
	        if(!method.toUpperCase().equals("GET"))
	        {
	        	System.out.println("I don't understand requests other than GET");
	        	out.flush();
	        	sock.close();
	        	System.out.println("Ended a thread.");
	        	server.unregisterThread(this);
	        	return;
	        }
	        
	        path = java.net.URLDecoder.decode(path, "UTF-8");
	        
	        path = collapseSlashes(path);
	        
	        System.out.println("Method = \"" + method + "\"");
	        System.out.println("Path = \"" + path + "\"");
	
	        // Send the response
	        // Send the headers
	        
	        if(path.equals("/"))
	        	path = "/dir/";
	        
	        if(path.equals("/dir"))
	        	path = "/dir/";
	        
	        //if(path.equals("/"))
	        if(path.startsWith("/dir/") || path.startsWith("dir/"))
	        {
		        out.println("HTTP/1.0 200 OK");
		        out.println("Content-Type: text/html");
		        out.println("Server: InstaDir");
		        // this blank line signals the end of the headers
		        out.println("");
		        // Send the HTML page
		        //out.println("<H1>Welcome to the Ultra Mini-WebServer</H2>");
		        
		        System.out.println("PATH = \"" + path + "\"");
		        
		        String realDir = path.substring(4, path.length());
		        if(realDir.startsWith("/")) realDir = realDir.substring(1, realDir.length());
		        
		        System.out.println("REAL DIR = \"" + realDir + "\"");
		        IDInstance.IDFile dir = env.traversePath(realDir);
		        
		        if(dir != null)
		        {
		        	IndexConstructor.constructIndex(dir, out);
		        }
		        //else
		        //{
		        //	out.print("FAIL FAIL FAIL");
		        //}
		        
		        out.flush();
		        sock.close();
	        }
	        if(path.startsWith("/file/") || path.startsWith("file/"))
	        {
		        
		        System.out.println("FILE = \"" + path + "\"");
		        String realFile = path.substring(5, path.length());
		        if(realFile.startsWith("/")) realFile = realFile.substring(1, realFile.length());
		        
		        System.out.println("REAL FILE = \"" + realFile + "\"");
		        IDInstance.IDFile dir = env.traversePath(realFile);
		        
		        if(dir != null && !dir.dir)
		        {
		        	//IndexConstructor.constructIndex(dir, out);
		        	if(server.getServerListener() != null)
		        		server.getServerListener().transferStart(sock.getInetAddress().toString(), dir.relPath);
		        	
		        	serveFile(dir.file, sock.getOutputStream(), false);
		        	
		        	if(server.getServerListener() != null)
		        		server.getServerListener().transferEnd(sock.getInetAddress().toString(), dir.relPath);
		        }
		        else
		        {
		        	out.print("FAIL FAIL FAIL");
		        	out.flush();
			        sock.close();
		        }
	        }
	        else if(path.startsWith("/res/") || path.startsWith("res/") || path.equals("/favicon.ico"))
	        {
	        	//serveFile(Style.getInstance().getFile(path), sock.getOutputStream(), false);
	        	serveStream(path, Style.getInstance().getFileStream(path), sock.getOutputStream(), false);
	        }
	        else
	        {
	        	out.println("HTTP/1.0 404 Not Found");
		        out.println("Content-Type: text/html");
		        out.println("Server: InstaDir");
		        // this blank line signals the end of the headers
		        out.println();
		        out.println("404 - NOT FOUND");
		        out.flush();
		        sock.close();
	        }
		} 
		catch (IOException e) 
		{
			System.out.println("Some kind of fail : " + e.getMessage());
		}
		
		System.out.println("Ended a thread.");
		server.unregisterThread(this);
	}
	
}
